﻿using System;
using System.Globalization;
using System.Reflection;
using Library.Linq;

namespace Library.Reflection
{
    public class TypeInferenceBinder : Binder
    {
        public override FieldInfo BindToField(BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo culture)
        {
            return Type.DefaultBinder.BindToField(bindingAttr, match, value, culture);
        }

        public override MethodBase BindToMethod(BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] names, out object state)
        {
            return Type.DefaultBinder.BindToMethod(bindingAttr, match, ref args, modifiers, culture, names, out state);
        }

        public override object ChangeType(object value, Type type, CultureInfo culture)
        {
            return Type.DefaultBinder.ChangeType(value, type, culture);
        }

        public override void ReorderArgumentArray(ref object[] args, object state)
        {
            Type.DefaultBinder.ReorderArgumentArray(ref args, state);
        }

        public MethodBase SelectMethod(BindingFlags bindingAttr, System.Collections.Generic.IEnumerable<MethodBase> match, Type[] types, ParameterModifier[] modifiers)
        {
            throw new NotImplementedException();
            //MethodInfo returnValue = null;
            //foreach (var method in match)
            //{
            //    MethodInfo methodInfo = (MethodInfo)method;

            //    if (method.IsGenericMethodDefinition)
            //    {
            //        Type[] gtp;

            //        if (TypeInference.Infer(methodInfo, types, modifiers, out gtp))
            //        {
            //            methodInfo = methodInfo.MakeGenericMethod(gtp);
            //        }
            //        else continue;
            //    }

            //    if (returnValue == null) returnValue = methodInfo;
            //    else
            //    {
            //        int compare = TypeInference.Compare(methodInfo, returnValue, types, modifiers);
            //        // ...
            //    }
            //}
            //return returnValue;
        }

        public override MethodBase SelectMethod(BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers)
        {
            throw new NotImplementedException();
        }

        public override PropertyInfo SelectProperty(BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers)
        {
            return Type.DefaultBinder.SelectProperty(bindingAttr, match, returnType, indexes, modifiers);
        }
    }

    //public class TypeInferenceContext
    //{
    //    protected enum BoundKind
    //    {
    //        Exact = 0,
    //        Lower = 1,
    //        Upper = 2
    //    }

    //    protected class BoundInfo : IEquatable<BoundInfo>
    //    {
    //        public readonly Type Type;
    //        public readonly BoundKind Kind;

    //        public BoundInfo(Type type, BoundKind kind)
    //        {
    //            this.Type = type;
    //            this.Kind = kind;
    //        }

    //        public override int GetHashCode()
    //        {
    //            return Type.GetHashCode();
    //        }

    //        //public virtual System.Linq.Expressions.Expression GetTypeExpression()
    //        //{
    //        //    return new TypeExpression(Type, Location.Null);
    //        //}

    //        #region IEquatable<BoundInfo> Members

    //        public virtual bool Equals(BoundInfo other)
    //        {
    //            return Type == other.Type && Kind == other.Kind;
    //        }

    //        #endregion
    //    }

    //    readonly Type[] tp_args;
    //    readonly Type[] fixed_types;
    //    readonly Library.Collections.Generic.List<BoundInfo>[] bounds;
    //    bool failed;

    //    // TODO MemberCache: Could it be TypeParameterSpec[] ??
    //    public TypeInferenceContext(Type[] typeArguments)
    //    {
    //        if (typeArguments.Length == 0)
    //            throw new ArgumentException("Empty generic arguments");

    //        fixed_types = new Type[typeArguments.Length];
    //        for (int i = 0; i < typeArguments.Length; ++i)
    //        {
    //            if (typeArguments[i].IsGenericParameter)
    //            {
    //                if (bounds == null)
    //                {
    //                    bounds = new Library.Collections.Generic.List<BoundInfo>[typeArguments.Length];
    //                    tp_args = new Type[typeArguments.Length];
    //                }
    //                tp_args[i] = typeArguments[i];
    //            }
    //            else
    //            {
    //                fixed_types[i] = typeArguments[i];
    //            }
    //        }
    //    }

    //    // 
    //    // Used together with AddCommonTypeBound fo implement
    //    // 7.4.2.13 Finding the best common type of a set of expressions
    //    //
    //    public TypeInferenceContext()
    //    {
    //        fixed_types = new Type[1];
    //        tp_args = new Type[1];
    //        tp_args[0] = InternalType.Arglist; // it can be any internal type
    //        bounds = new System.Collections.Generic.List<BoundInfo>[1];
    //    }

    //    public Type[] InferredTypeArguments
    //    {
    //        get
    //        {
    //            return fixed_types;
    //        }
    //    }

    //    public void AddCommonTypeBound(Type type)
    //    {
    //        AddToBounds(new BoundInfo(type, BoundKind.Lower), 0);
    //    }

    //    protected void AddToBounds(BoundInfo bound, int index)
    //    {
    //        //
    //        // Some types cannot be used as type arguments
    //        //
    //        if (bound.Type == typeof(void) || bound.Type.IsPointer || bound.Type.IsSpecialRuntimeType ||
    //            bound.Type == InternalType.MethodGroup || bound.Type == InternalType.AnonymousMethod)
    //            return;

    //        var a = bounds[index];
    //        if (a == null)
    //        {
    //            a = new Library.Collections.Generic.List<BoundInfo>(2);
    //            a.Add(bound);
    //            bounds[index] = a;
    //            return;
    //        }

    //        if (a.Contains(bound))
    //            return;

    //        a.Add(bound);
    //    }

    //    bool AllTypesAreFixed(Type[] types)
    //    {
    //        foreach (Type t in types)
    //        {
    //            if (t.IsGenericParameter)
    //            {
    //                if (!IsFixed(t))
    //                    return false;
    //                continue;
    //            }

    //            if (TypeManager.IsGenericType(t))
    //                return AllTypesAreFixed(TypeManager.GetTypeArguments(t));
    //        }

    //        return true;
    //    }

    //    bool AllTypesAreFixed(System.Collections.Generic.IEnumerable<Type> types)
    //    {
    //        foreach (Type t in types)
    //        {
    //            if (t.IsGenericParameter)
    //            {
    //                if (!IsFixed(t))
    //                    return false;
    //                continue;
    //            }

    //            if (TypeManager.IsGenericType(t))
    //                return AllTypesAreFixed(TypeManager.GetTypeArguments(t));
    //        }

    //        return true;
    //    }

    //    //
    //    // 26.3.3.8 Exact Inference
    //    //
    //    public int ExactInference(Type u, Type v)
    //    {
    //        // If V is an array type
    //        if (v.IsArray)
    //        {
    //            if (!u.IsArray)
    //                return 0;

    //            if (u.GetArrayRank() != v.GetArrayRank())
    //                return 0;

    //            return ExactInference(u.GetElementType(), v.GetElementType());
    //        }

    //        // If V is constructed type and U is constructed type
    //        if (v.IsGenericType)
    //        {
    //            if (!u.IsGenericType || v.GetGenericTypeDefinition() != u.GetGenericTypeDefinition())
    //                return 0;

    //            Type[] ga_u = u.GetGenericArguments(); // TypeManager.GetTypeArguments(u);
    //            Type[] ga_v = v.GetGenericArguments(); // TypeManager.GetTypeArguments(v);
    //            if (ga_u.Length != ga_v.Length)
    //                return 0;

    //            int score = 0;
    //            for (int i = 0; i < ga_u.Length; ++i)
    //                score += ExactInference(ga_u[i], ga_v[i]);

    //            return score > 0 ? 1 : 0;
    //        }

    //        // If V is one of the unfixed type arguments
    //        int pos = IsUnfixed(v);
    //        if (pos == -1)
    //            return 0;

    //        AddToBounds(new BoundInfo(u, BoundKind.Exact), pos);
    //        return 1;
    //    }

    //    public bool FixAllTypes()
    //    {
    //        for (int i = 0; i < tp_args.Length; ++i)
    //        {
    //            if (!FixType(i))
    //                return false;
    //        }
    //        return true;
    //    }

    //    //
    //    // All unfixed type variables Xi are fixed for which all of the following hold:
    //    // a, There is at least one type variable Xj that depends on Xi
    //    // b, Xi has a non-empty set of bounds
    //    // 
    //    public bool FixDependentTypes(ref bool fixed_any)
    //    {
    //        for (int i = 0; i < tp_args.Length; ++i)
    //        {
    //            if (fixed_types[i] != null)
    //                continue;

    //            if (bounds[i] == null)
    //                continue;

    //            if (!FixType(i))
    //                return false;

    //            fixed_any = true;
    //        }

    //        return true;
    //    }

    //    //
    //    // All unfixed type variables Xi which depend on no Xj are fixed
    //    //
    //    public bool FixIndependentTypeArguments(Type[] methodParameters, ref bool fixed_any)
    //    {
    //        var types_to_fix = new List<TypeSpec>(tp_args);
    //        for (int i = 0; i < methodParameters.Length; ++i)
    //        {
    //            TypeSpec t = methodParameters[i];

    //            if (!t.IsDelegate)
    //            {
    //                if (!t.IsExpressionTreeType)
    //                    continue;

    //                t = TypeManager.GetTypeArguments(t)[0];
    //            }

    //            if (t.IsGenericParameter)
    //                continue;

    //            var invoke = Delegate.GetInvokeMethod(t);
    //            TypeSpec rtype = invoke.ReturnType;
    //            while (rtype.IsArray)
    //                rtype = ((ArrayContainer)rtype).Element;

    //            if (!rtype.IsGenericParameter && !TypeManager.IsGenericType(rtype))
    //                continue;

    //            // Remove dependent types, they cannot be fixed yet
    //            RemoveDependentTypes(types_to_fix, rtype);
    //        }

    //        foreach (TypeSpec t in types_to_fix)
    //        {
    //            if (t == null)
    //                continue;

    //            int idx = IsUnfixed(t);
    //            if (idx >= 0 && !FixType(ec, idx))
    //            {
    //                return false;
    //            }
    //        }

    //        fixed_any = types_to_fix.Count > 0;
    //        return true;
    //    }

    //    //
    //    // 26.3.3.10 Fixing
    //    //
    //    public bool FixType(int i)
    //    {
    //        // It's already fixed
    //        if (fixed_types[i] != null)
    //            throw new InternalErrorException("Type argument has been already fixed");

    //        if (failed)
    //            return false;

    //        var candidates = bounds[i];
    //        if (candidates == null)
    //            return false;

    //        if (candidates.Count == 1)
    //        {
    //            Type t = candidates[0].Type;
    //            if (t == InternalType.NullLiteral)
    //                return false;

    //            fixed_types[i] = t;
    //            return true;
    //        }

    //        //
    //        // Determines a unique type from which there is
    //        // a standard implicit conversion to all the other
    //        // candidate types.
    //        //
    //        Type best_candidate = null;
    //        ulong cii;
    //        ulong candidates_count = candidates.Count;
    //        for (ulong ci = 0; ci < candidates_count; ++ci)
    //        {
    //            BoundInfo bound = candidates[ci];
    //            for (cii = 0; cii < candidates_count; ++cii)
    //            {
    //                if (cii == ci)
    //                    continue;

    //                BoundInfo cbound = candidates[cii];

    //                // Same type parameters with different bounds
    //                if (cbound.Type == bound.Type)
    //                {
    //                    if (bound.Kind != BoundKind.Exact)
    //                        bound = cbound;

    //                    continue;
    //                }

    //                if (bound.Kind == BoundKind.Exact || cbound.Kind == BoundKind.Exact)
    //                {
    //                    if (cbound.Kind == BoundKind.Lower)
    //                    {
    //                        if (!Convert.ImplicitConversionExists(cbound.GetTypeExpression(), bound.Type))
    //                        {
    //                            break;
    //                        }

    //                        continue;
    //                    }
    //                    if (cbound.Kind == BoundKind.Upper)
    //                    {
    //                        if (!Convert.ImplicitConversionExists(bound.GetTypeExpression(), cbound.Type))
    //                        {
    //                            break;
    //                        }

    //                        continue;
    //                    }

    //                    if (bound.Kind != BoundKind.Exact)
    //                    {
    //                        if (!Convert.ImplicitConversionExists(bound.GetTypeExpression(), cbound.Type))
    //                        {
    //                            break;
    //                        }

    //                        bound = cbound;
    //                        continue;
    //                    }

    //                    break;
    //                }

    //                if (bound.Kind == BoundKind.Lower)
    //                {
    //                    if (cbound.Kind == BoundKind.Lower)
    //                    {
    //                        if (!Convert.ImplicitConversionExists(cbound.GetTypeExpression(), bound.Type))
    //                        {
    //                            break;
    //                        }
    //                    }
    //                    else
    //                    {
    //                        if (!Convert.ImplicitConversionExists(bound.GetTypeExpression(), cbound.Type))
    //                        {
    //                            break;
    //                        }

    //                        bound = cbound;
    //                    }

    //                    continue;
    //                }

    //                if (bound.Kind == BoundKind.Upper)
    //                {
    //                    if (!Convert.ImplicitConversionExists(bound.GetTypeExpression(), cbound.Type))
    //                    {
    //                        break;
    //                    }
    //                }
    //                else
    //                {
    //                    throw new NotImplementedException("variance conversion");
    //                }
    //            }

    //            if (cii != candidates_count)
    //                continue;

    //            //
    //            // We already have the best candidate, break if thet are different
    //            //
    //            // Dynamic is never ambiguous as we prefer dynamic over other best candidate types
    //            //
    //            if (best_candidate != null)
    //            {

    //                if (best_candidate.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
    //                    continue;

    //                if (bound.Type.BuiltinType != BuiltinTypeSpec.Type.Dynamic && best_candidate != bound.Type)
    //                    return false;
    //            }

    //            best_candidate = bound.Type;
    //        }

    //        if (best_candidate == null)
    //            return false;

    //        fixed_types[i] = best_candidate;
    //        return true;
    //    }

    //    public bool HasBounds(int pos)
    //    {
    //        return bounds[pos] != null;
    //    }

    //    //
    //    // Uses inferred or partially infered types to inflate delegate type argument. Returns
    //    // null when type parameter has not been fixed
    //    //
    //    public Type InflateGenericArgument(IModuleContext context, Type parameter)
    //    {
    //        //var tp = parameter as TypeParameterSpec;
    //        //if (tp != null)
    //        if (parameter.IsGenericParameter)
    //        {
    //            //
    //            // Type inference works on generic arguments (MVAR) only
    //            //
    //            //if (!tp.IsMethodOwned)
    //            //return parameter;

    //            //
    //            // Ensure the type parameter belongs to same container
    //            //
    //            if (parameter.GenericParameterPosition < tp_args.Length && tp_args[parameter.GenericParameterPosition] == parameter)
    //                return fixed_types[parameter.GenericParameterPosition] ?? parameter;

    //            return parameter;
    //        }

    //        var gt = parameter as InflatedTypeSpec;
    //        if (gt != null)
    //        {
    //            var inflated_targs = new Type[gt.TypeArguments.Length];
    //            for (int ii = 0; ii < inflated_targs.Length; ++ii)
    //            {
    //                var inflated = InflateGenericArgument(context, gt.TypeArguments[ii]);
    //                if (inflated == null)
    //                    return null;

    //                inflated_targs[ii] = inflated;
    //            }

    //            return gt.GetDefinition().MakeGenericType(context, inflated_targs);
    //        }

    //        //var ac = parameter as ArrayContainer;
    //        //if (ac != null)
    //        if (parameter.IsArray)
    //        {
    //            var inflated = InflateGenericArgument(context, parameter.GetElementType());
    //            if (inflated != parameter.GetElementType())
    //                return inflated.MakeArrayType(); // ArrayContainer.MakeType(context.Module, inflated);
    //        }

    //        return parameter;
    //    }

    //    //
    //    // Tests whether all delegate input arguments are fixed and generic output type
    //    // requires output type inference 
    //    //
    //    public bool IsReturnTypeNonDependent(MethodInfo invoke, Type returnType)
    //    {
    //        while (returnType.IsArray)
    //            returnType = returnType.GetElementType();

    //        if (returnType.IsGenericParameter)
    //        {
    //            if (IsFixed(returnType))
    //                return false;
    //        }
    //        else if (returnType.IsGenericType)
    //        {
    //            if (returnType.IsDelegate)
    //            {
    //                invoke = Delegate.GetInvokeMethod(returnType);
    //                return IsReturnTypeNonDependent(ec, invoke, invoke.ReturnType);
    //            }

    //            Type[] g_args = returnType.GetGenericArguments();

    //            // At least one unfixed return type has to exist 
    //            if (AllTypesAreFixed(g_args))
    //                return false;
    //        }
    //        else
    //        {
    //            return false;
    //        }

    //        // All generic input arguments have to be fixed
    //        //AParametersCollection d_parameters = invoke.GetParameters();
    //        return AllTypesAreFixed(System.Linq.Enumerable.Select(invoke.GetParameters(), n => n.ParameterType));
    //    }

    //    bool IsFixed(Type type)
    //    {
    //        return IsUnfixed(type) == -1;
    //    }

    //    int IsUnfixed(Type type)
    //    {
    //        if (!type.IsGenericParameter)
    //            return -1;

    //        for (int i = 0; i < tp_args.Length; ++i)
    //        {
    //            if (tp_args[i] == type)
    //            {
    //                if (fixed_types[i] != null)
    //                    break;

    //                return i;
    //            }
    //        }

    //        return -1;
    //    }

    //    //
    //    // 26.3.3.9 Lower-bound Inference
    //    //
    //    public int LowerBoundInference(Type u, Type v)
    //    {
    //        return LowerBoundInference(u, v, false);
    //    }

    //    //
    //    // Lower-bound (false) or Upper-bound (true) inference based on inversed argument
    //    //
    //    int LowerBoundInference(Type u, Type v, bool inversed)
    //    {
    //        // If V is one of the unfixed type arguments
    //        int pos = IsUnfixed(v);
    //        if (pos != -1)
    //        {
    //            AddToBounds(new BoundInfo(u, inversed ? BoundKind.Upper : BoundKind.Lower), pos);
    //            return 1;
    //        }

    //        // If U is an array type
    //        //var u_ac = u as ArrayContainer;
    //        //if (u_ac != null)
    //        if (u.IsArray)
    //        {
    //            //var v_ac = v as ArrayContainer;
    //            //if (v_ac != null)
    //            if (v.IsArray)
    //            {
    //                //if (u_ac.Rank != v_ac.Rank)
    //                if (u.GetArrayRank() != v.GetArrayRank())
    //                    return 0;

    //                if (u.HasElementType && u.GetElementType().IsValueType)
    //                    return ExactInference(u.GetElementType(), v.GetElementType());

    //                return LowerBoundInference(u.GetElementType(), v.GetElementType(), inversed);
    //            }

    //            if (u.GetArrayRank() != 1 || !v.IsGenericIterateInterface)
    //                return 0;

    //            var v_i = v.GetGenericArguments()[0];// TypeManager.GetTypeArguments(v)[0];
    //            if (u.GetElementType().IsValueType) //(TypeSpec.IsValueType(u_ac.Element))
    //                return ExactInference(u.GetElementType(), v_i);

    //            return LowerBoundInference(u.GetElementType(), v_i);
    //        }

    //        if (v.IsGenericOrParentIsGeneric)
    //        {
    //            //
    //            // if V is a constructed type C<V1..Vk> and there is a unique type C<U1..Uk>
    //            // such that U is identical to, inherits from (directly or indirectly),
    //            // or implements (directly or indirectly) C<U1..Uk>
    //            //
    //            var u_candidates = new List<TypeSpec>();
    //            var open_v = v.MemberDefinition;

    //            for (TypeSpec t = u; t != null; t = t.BaseType)
    //            {
    //                if (open_v == t.MemberDefinition)
    //                    u_candidates.Add(t);

    //                //
    //                // Using this trick for dynamic type inference, the spec says the type arguments are "unknown" but
    //                // that would complicate the process a lot, instead I treat them as dynamic
    //                //
    //                if (t.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
    //                    u_candidates.Add(t);

    //                if (t.Interfaces != null)
    //                {
    //                    foreach (var iface in t.Interfaces)
    //                    {
    //                        if (open_v == iface.MemberDefinition)
    //                            u_candidates.Add(iface);
    //                    }
    //                }
    //            }

    //            TypeSpec[] unique_candidate_targs = null;
    //            var ga_v = TypeSpec.GetAllTypeArguments(v);
    //            foreach (TypeSpec u_candidate in u_candidates)
    //            {
    //                //
    //                // The unique set of types U1..Uk means that if we have an interface I<T>,
    //                // class U : I<int>, I<long> then no type inference is made when inferring
    //                // type I<T> by applying type U because T could be int or long
    //                //
    //                if (unique_candidate_targs != null)
    //                {
    //                    TypeSpec[] second_unique_candidate_targs = TypeSpec.GetAllTypeArguments(u_candidate);
    //                    if (TypeSpecComparer.Equals(unique_candidate_targs, second_unique_candidate_targs))
    //                    {
    //                        unique_candidate_targs = second_unique_candidate_targs;
    //                        continue;
    //                    }

    //                    //
    //                    // This should always cause type inference failure
    //                    //
    //                    failed = true;
    //                    return 1;
    //                }

    //                //
    //                // A candidate is dynamic type expression, to simplify things use dynamic
    //                // for all type parameter of this type. For methods like this one
    //                // 
    //                // void M<T, U> (IList<T>, IList<U[]>)
    //                //
    //                // dynamic becomes both T and U when the arguments are of dynamic type
    //                //
    //                if (u_candidate.BuiltinType == BuiltinTypeSpec.Type.Dynamic)
    //                {
    //                    unique_candidate_targs = new TypeSpec[ga_v.Length];
    //                    for (int i = 0; i < unique_candidate_targs.Length; ++i)
    //                        unique_candidate_targs[i] = u_candidate;
    //                }
    //                else
    //                {
    //                    unique_candidate_targs = TypeSpec.GetAllTypeArguments(u_candidate);
    //                }
    //            }

    //            if (unique_candidate_targs != null)
    //            {
    //                int score = 0;
    //                int tp_index = -1;
    //                TypeParameterSpec[] tps = null;

    //                for (int i = 0; i < unique_candidate_targs.Length; ++i)
    //                {
    //                    if (tp_index < 0)
    //                    {
    //                        while (v.Arity == 0)
    //                            v = v.DeclaringType;

    //                        tps = v.MemberDefinition.TypeParameters;
    //                        tp_index = tps.Length - 1;
    //                    }

    //                    Variance variance = tps[tp_index--].Variance;

    //                    TypeSpec u_i = unique_candidate_targs[i];
    //                    if (variance == Variance.None || TypeSpec.IsValueType(u_i))
    //                    {
    //                        if (ExactInference(u_i, ga_v[i]) == 0)
    //                            ++score;
    //                    }
    //                    else
    //                    {
    //                        bool upper_bound = (variance == Variance.Contravariant && !inversed) ||
    //                            (variance == Variance.Covariant && inversed);

    //                        if (LowerBoundInference(u_i, ga_v[i], upper_bound) == 0)
    //                            ++score;
    //                    }
    //                }

    //                return score;
    //            }
    //        }

    //        return 0;
    //    }

    //    //
    //    // 26.3.3.6 Output Type Inference
    //    //
    //    public int OutputTypeInference(Expression e, Type t)
    //    {
    //        // If e is a lambda or anonymous method with inferred return type
    //        AnonymousMethodExpression ame = e as AnonymousMethodExpression;
    //        if (ame != null)
    //        {
    //            Type rt = ame.InferReturnType(ec, this, t);
    //            var invoke = Delegate.GetInvokeMethod(t);

    //            if (rt == null)
    //            {
    //                AParametersCollection pd = invoke.Parameters;
    //                return ame.Parameters.Count == pd.Count ? 1 : 0;
    //            }

    //            Type rtype = invoke.ReturnType;
    //            return LowerBoundInference(rt, rtype) + 1;
    //        }

    //        //
    //        // if E is a method group and T is a delegate type or expression tree type
    //        // return type Tb with parameter types T1..Tk and return type Tb, and overload
    //        // resolution of E with the types T1..Tk yields a single method with return type U,
    //        // then a lower-bound inference is made from U for Tb.
    //        //
    //        if (e is MethodGroupExpr)
    //        {
    //            if (!t.IsDelegate)
    //            {
    //                if (!t.IsExpressionTreeType)
    //                    return 0;

    //                t = TypeManager.GetTypeArguments(t)[0];
    //            }

    //            var invoke = Delegate.GetInvokeMethod(t);
    //            TypeSpec rtype = invoke.ReturnType;

    //            if (!rtype.IsGenericParameter && !TypeManager.IsGenericType(rtype))
    //                return 0;

    //            // LAMESPEC: Standard does not specify that all methodgroup arguments
    //            // has to be fixed but it does not specify how to do recursive type inference
    //            // either. We choose the simple option and infer return type only
    //            // if all delegate generic arguments are fixed.
    //            TypeSpec[] param_types = new TypeSpec[invoke.Parameters.Count];
    //            for (int i = 0; i < param_types.Length; ++i)
    //            {
    //                var inflated = InflateGenericArgument(ec, invoke.Parameters.Types[i]);
    //                if (inflated == null)
    //                    return 0;

    //                if (IsUnfixed(inflated) >= 0)
    //                    return 0;

    //                param_types[i] = inflated;
    //            }

    //            MethodGroupExpr mg = (MethodGroupExpr)e;
    //            Arguments args = DelegateCreation.CreateDelegateMethodArguments(invoke.Parameters, param_types, e.Location);
    //            mg = mg.OverloadResolve(ec, ref args, null, OverloadResolver.Restrictions.CovariantDelegate | OverloadResolver.Restrictions.ProbingOnly);
    //            if (mg == null)
    //                return 0;

    //            return LowerBoundInference(mg.BestCandidateReturnType, rtype) + 1;
    //        }

    //        //
    //        // if e is an expression with type U, then
    //        // a lower-bound inference is made from U for T
    //        //
    //        return LowerBoundInference(e.Type, t) * 2;
    //    }

    //    void RemoveDependentTypes(Library.Collections.Generic.List<Type> types, Type returnType)
    //    {
    //        int idx = IsUnfixed(returnType);
    //        if (idx >= 0)
    //        {
    //            types[idx] = null;
    //            return;
    //        }

    //        if (TypeManager.IsGenericType(returnType))
    //        {
    //            foreach (TypeSpec t in TypeManager.GetTypeArguments(returnType))
    //            {
    //                RemoveDependentTypes(types, t);
    //            }
    //        }
    //    }

    //    public bool UnfixedVariableExists
    //    {
    //        get
    //        {
    //            foreach (Type ut in fixed_types)
    //            {
    //                if (ut == null)
    //                    return true;
    //            }

    //            return false;
    //        }
    //    }
    //}

    //class TypeInference
    //{
    //    public static bool Infer(MethodInfo method, Type[] types, ParameterModifier[] modifiers, out Type[] genericTypeParameters)
    //    {
    //        throw new NotImplementedException();
    //    }

    //    public static int Compare(MethodInfo x, MethodInfo y, Type[] types, ParameterModifier[] modifiers)
    //    {
    //        throw new NotImplementedException();
    //    }

    //    //
    //    // Tracks successful rate of type inference
    //    //
    //    int score = int.MaxValue;
    //    readonly Arguments arguments;
    //    readonly int arg_count;

    //    public TypeInference(Arguments arguments)
    //    {
    //        this.arguments = arguments;
    //        if (arguments != null)
    //            arg_count = arguments.Count;
    //    }

    //    public int InferenceScore
    //    {
    //        get
    //        {
    //            return score;
    //        }
    //    }

    //    public Type[] InferMethodArguments(MethodBase method)
    //    {
    //        var method_generic_args = method.GetGenericArguments();// GenericDefinition.TypeParameters;
    //        TypeInferenceContext context = new TypeInferenceContext(method_generic_args);
    //        if (!context.UnfixedVariableExists)
    //            return Type.EmptyTypes; // TypeSpec.EmptyTypes;

    //        //AParametersCollection pd = method.GetParameters(); // Parameters;
    //        if (!InferInPhases(ec, context, method.GetParameters()))
    //            return null;

    //        return context.InferredTypeArguments;
    //    }

    //    //
    //    // Implements method type arguments inference
    //    //
    //    bool InferInPhases(TypeInferenceContext tic, System.Reflection.ParameterInfo[] methodParameters)
    //    {
    //        int params_arguments_start;
    //        if (methodParameters.Length > 0)
    //        {
    //            params_arguments_start = methodParameters.Length - 1;
    //        }
    //        else
    //        {
    //            params_arguments_start = arg_count;
    //        }

    //        System.Type[] ptypes = methodParameters.Select(n => n.ParameterType).ToArray(); // Types;

    //        //
    //        // The first inference phase
    //        //
    //        System.Type method_parameter = null;
    //        for (int i = 0; i < arg_count; i++)
    //        {
    //            Argument a = arguments[i];
    //            if (a == null)
    //                continue;

    //            if (i < params_arguments_start)
    //            {
    //                method_parameter = methodParameters[i].ParameterType;// Types[i];
    //            }
    //            else if (i == params_arguments_start)
    //            {
    //                if (arg_count == params_arguments_start + 1 && TypeManager.HasElementType(a.Type))
    //                    method_parameter = methodParameters[params_arguments_start].ParameterType;
    //                else
    //                    method_parameter = TypeManager.GetElementType(methodParameters[params_arguments_start].ParameterType);

    //                ptypes = (System.Type[])ptypes.Clone();
    //                ptypes[i] = method_parameter;
    //            }

    //            //
    //            // When a lambda expression, an anonymous method
    //            // is used an explicit argument type inference takes a place
    //            //
    //            AnonymousMethodExpression am = a.Expr as AnonymousMethodExpression;
    //            if (am != null)
    //            {
    //                if (am.ExplicitTypeInference(tic, method_parameter))
    //                    --score;
    //                continue;
    //            }

    //            if (a.IsByRef)
    //            {
    //                score -= tic.ExactInference(a.Type, method_parameter);
    //                continue;
    //            }

    //            if (a.Expr.Type == InternalType.NullLiteral)
    //                continue;

    //            if (method_parameter.IsValueType)
    //            {
    //                score -= tic.LowerBoundInference(a.Type, method_parameter);
    //                continue;
    //            }

    //            //
    //            // Otherwise an output type inference is made
    //            //
    //            score -= tic.OutputTypeInference(a.Expr, method_parameter);
    //        }

    //        //
    //        // Part of the second phase but because it happens only once
    //        // we don't need to call it in cycle
    //        //
    //        bool fixed_any = false;
    //        if (!tic.FixIndependentTypeArguments(ec, ptypes, ref fixed_any))
    //            return false;

    //        return DoSecondPhase(ec, tic, ptypes, !fixed_any);
    //    }

    //    bool DoSecondPhase(TypeInferenceContext tic, Type[] methodParameters, bool fixDependent)
    //    {
    //        bool fixed_any = false;
    //        if (fixDependent && !tic.FixDependentTypes(ref fixed_any))
    //            return false;

    //        // If no further unfixed type variables exist, type inference succeeds
    //        if (!tic.UnfixedVariableExists)
    //            return true;

    //        if (!fixed_any && fixDependent)
    //            return false;

    //        // For all arguments where the corresponding argument output types
    //        // contain unfixed type variables but the input types do not,
    //        // an output type inference is made
    //        for (int i = 0; i < arg_count; i++)
    //        {
    //            // Align params arguments
    //            TypeSpec t_i = methodParameters[i >= methodParameters.Length ? methodParameters.Length - 1 : i];

    //            if (!t_i.IsDelegate)
    //            {
    //                if (!t_i.IsExpressionTreeType)
    //                    continue;

    //                t_i = TypeManager.GetTypeArguments(t_i)[0];
    //            }

    //            var mi = Delegate.GetInvokeMethod(t_i);
    //            TypeSpec rtype = mi.ReturnType;

    //            if (tic.IsReturnTypeNonDependent(mi, rtype))
    //            {
    //                // It can be null for default arguments
    //                if (arguments[i] == null)
    //                    continue;

    //                score -= tic.OutputTypeInference(arguments[i].Expr, t_i);
    //            }
    //        }

    //        return DoSecondPhase(tic, methodParameters, true);
    //    }
    //}
}
